/*****************************************************
** Author: 
** Date: 2020-02-27
*
*****************************************************/

#include <string.h>
#include <stddef.h>
#include <math.h>
#include "comm.h"


struct tlv {
    unsigned char tag;
    unsigned char len;
    unsigned char value[0];
};


#define BL3361_FREQ_BANK_REG    0x18
#define BL3361_TX_BANK_REG      0x55

/**
 * @brief
 * @param  None
 * @retval None
 */
static int bl3361_get_freq_mapping(double freq_rf, unsigned char *vco_back, \
                    unsigned char *divx_code, unsigned char *divider)
{
    int i = 0;
    const unsigned char mapping[][3] = {
        // VCO_BANK, DIVX_CODE, DEVIDER
        {0x01,      0x01,       4},     // 420 - 510 MHz
        {0x01,      0x05,       6}      // 280 - 340 MHz
    };

    // tc_log("freq_rf %f", freq_rf);
    // hexdump(&freq_rf, sizeof(double), "freq_rf");

    if (freq_rf >= 430.0F && freq_rf <= 440.0F)
        i = 0;
    else if (freq_rf >= 305.0F && freq_rf <= 340.0F)
        i = 1;
    else {
        tc_log_err("freq_rf %f err", freq_rf);
        return -1;
    }

    *vco_back  = mapping[i][0];
    *divx_code = mapping[i][1];
    *divider   = mapping[i][2];
    return 0;
}

/*  this function only could be used at x86 with double
 *  otherwise will import errors
 */
static int bl3361_frequency_bank_calc(double freq_rf, unsigned char *output, int maxsize)
{
    if (maxsize < 8) {
        tc_log_err("maxsize %d err", maxsize);
        return -1;
    }

    unsigned char vco_back;
    unsigned char divx_code;
    unsigned char divider;
    int ret;

    if ((ret = bl3361_get_freq_mapping(freq_rf, &vco_back, &divx_code, &divider)) < 0) {
        tc_log_err("bl3361_get_freq_mapping ret %d", ret);
        return -2;
    }

    // tc_log("vco_back 0x%x divx_code 0x%x divider 0x%x", vco_back, divx_code, divider);

    // RX
#if 0
    // this way will import errors
    double freq_lo_2 = freq_rf + 26 / 92;
    double nk_2 = freq_lo_2 * divider / 26;
#else
    double freq_lo = freq_rf * 1000000 + 26 * 1000000 / 92;
    double nk = freq_lo * divider / (26 * 1000000);
#endif
    // tc_log("freq_lo %f nk %f", freq_lo, nk);
    // hexdump(&freq_lo, sizeof(double), "freq_lo");
    // hexdump(&nk, sizeof(double), "nk");

    int n, k;
    n = (int)nk;
    k = (nk - n) * pow(2, 20);

    // tc_log("rx nk %f n %u k %u", nk, n, k);

    output[0] = n & 0xff;
    output[1] = k & 0xff;
    output[2] = (k >> 8) & 0xff;
    output[3] = (divx_code << 4) | ((k >> 16) & 0x0f);

    // TX
#if 0
    // this way will import errors
    nk_2 = freq_rf * divider / 26;
    tc_log("nk_2 %f", nk_2);
#else
    freq_lo = freq_rf * 1000000;
    nk = freq_lo * divider / (26 * 1000000);
#endif
    // tc_log("freq_lo %f nk %f", freq_lo, nk);

    n = (int)nk;
    k = (nk - n) * pow(2, 20);

    // tc_log("tx nk %f n %u k %u", nk, n, k);

    output[4] = n & 0xff;
    output[5] = k & 0xff;
    output[6] = (k >> 8) & 0xff;
    output[7] = (vco_back << 4) | ((k >> 16) & 0x0f);

    return 8;
}

static int bl3361_fill_calc_freq_reg(double freq_rf, unsigned char *output, int maxsize)
{
    unsigned char reg_data[] = {
        // [CMT Bank] 
        0x00, 0x66, 0xEC, 0x1D, 0x70, 0x80, 0x14, 0x08, 0x91, 0x02, 0x02, 0xD0,
        // [System Bank] 
        0xAE, 0xE0, 0x35, 0x00, 0x00, 0xF4, 0x10, 0xE2, 0x42, 0x20, 0x21, 0x81,
        // [Frequency Bank] 
        0x46, 0x83, 0x32, 0x57, 0x46, 0x62, 0x27, 0x16,
        // [Data Rate Bank] 
        0xD3, 0x64, 0xA0, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xC0, 0x28, 0x0A, 0x53, 0x17, 0x07, 0x3C, 0x2F, 0x00, 0x26, 0x05, 0x05,
        // [Baseband Bank] 
        0x10, 0x08, 0x00, 0xAA, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0x2D, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xFF, 0x00, 0x00, 0x1F, 0x10,
        // [TX Bank] 
        0x55, 0xB9, 0x04, 0x00, 0x03, 0x70, 0x00, 0xB4, 0x1E, 0x3F, 0x7F,   // default 315MHz +-
    };

    const unsigned char tx_bank_433[11] = {
        0x55, 0x26, 0x03, 0x00, 0x03, 0x70, 0x00, 0x8A, 0x18, 0x3F, 0x7F  // 433MHz +-
    };

    // tc_log("enter double freq_rf %lf MHz", freq_rf);

    if (maxsize < (sizeof(reg_data) * 2)) {
        tc_log_err("maxsize %d sizeof(reg_data) %d", maxsize, sizeof(reg_data));
        return -1;
    }

    if (bl3361_frequency_bank_calc(freq_rf, &reg_data[BL3361_FREQ_BANK_REG], 8) < 0)
        return -2;

    hexdump(&reg_data[BL3361_FREQ_BANK_REG], 8, "Frequency Bank %lfMHz", freq_rf);

    // update TX BANK
    if (freq_rf > 340.0F) 
        memcpy(&reg_data[BL3361_TX_BANK_REG], tx_bank_433, sizeof(tx_bank_433));

    int reg, offset;

    for (reg = 0x00, offset = 0; reg < 0x60; ++reg) {
        // tc_log("[0x%02x] 0x%02x offset %d output %p", reg, reg_data[reg], offset, output);
        output[offset++] = reg;
        output[offset++] = reg_data[reg];
    }
    return offset;
}

static int bl3361_frequency_bank_mapping(unsigned int freq_rf, unsigned char *output, int maxsize)
{
    const unsigned char fb_map[][8] = {
        {0x46, 0x83, 0x32, 0x57, 0x46, 0x62, 0x27, 0x16}, // 305.000000MHz, [0], total 35
        {0x46, 0xbe, 0xe3, 0x5a, 0x46, 0x9d, 0xd8, 0x19}, // 306.000000MHz
        {0x46, 0xf9, 0x94, 0x5e, 0x46, 0xd8, 0x89, 0x1d}, // 307.000000MHz
        {0x47, 0x34, 0x46, 0x52, 0x47, 0x13, 0x3b, 0x11}, // 308.000000MHz
        {0x47, 0x6f, 0xf7, 0x55, 0x47, 0x4e, 0xec, 0x14}, // 309.000000MHz
        {0x47, 0xab, 0xa8, 0x59, 0x47, 0x89, 0x9d, 0x18}, // 310.000000MHz
        {0x47, 0xe6, 0x59, 0x5d, 0x47, 0xc4, 0x4e, 0x1c}, // 311.000000MHz
        {0x48, 0x21, 0x0b, 0x51, 0x48, 0x00, 0x00, 0x10}, // 312.000000MHz
        {0x48, 0x5c, 0xbc, 0x54, 0x48, 0x3b, 0xb1, 0x13}, // 313.000000MHz
        {0x48, 0x97, 0x6d, 0x58, 0x48, 0x76, 0x62, 0x17}, // 314.000000MHz
        {0x48, 0xd2, 0x1e, 0x5c, 0x48, 0xb1, 0x13, 0x1b}, // 315.000000MHz
        {0x48, 0x0d, 0xd0, 0x5f, 0x48, 0xec, 0xc4, 0x1e}, // 316.000000MHz
        {0x49, 0x48, 0x81, 0x53, 0x49, 0x27, 0x76, 0x12}, // 317.000000MHz
        {0x49, 0x83, 0x32, 0x57, 0x49, 0x62, 0x27, 0x16}, // 318.000000MHz
        {0x49, 0xbe, 0xe3, 0x5a, 0x49, 0x9d, 0xd8, 0x19}, // 319.000000MHz
        {0x49, 0xf9, 0x94, 0x5e, 0x49, 0xd8, 0x89, 0x1d}, // 320.000000MHz
        {0x4a, 0x34, 0x46, 0x52, 0x4a, 0x13, 0x3b, 0x11}, // 321.000000MHz
        {0x4a, 0x6f, 0xf7, 0x55, 0x4a, 0x4e, 0xec, 0x14}, // 322.000000MHz
        {0x4a, 0xab, 0xa8, 0x59, 0x4a, 0x89, 0x9d, 0x18}, // 323.000000MHz
        {0x4a, 0xe6, 0x59, 0x5d, 0x4a, 0xc4, 0x4e, 0x1c}, // 324.000000MHz
        {0x4b, 0x21, 0x0b, 0x51, 0x4b, 0x00, 0x00, 0x10}, // 325.000000MHz
        {0x4b, 0x5c, 0xbc, 0x54, 0x4b, 0x3b, 0xb1, 0x13}, // 326.000000MHz
        {0x4b, 0x97, 0x6d, 0x58, 0x4b, 0x76, 0x62, 0x17}, // 327.000000MHz
        {0x4b, 0xd2, 0x1e, 0x5c, 0x4b, 0xb1, 0x13, 0x1b}, // 328.000000MHz
        {0x4b, 0x0d, 0xd0, 0x5f, 0x4b, 0xec, 0xc4, 0x1e}, // 329.000000MHz
        {0x4c, 0x48, 0x81, 0x53, 0x4c, 0x27, 0x76, 0x12}, // 330.000000MHz
        {0x4c, 0x83, 0x32, 0x57, 0x4c, 0x62, 0x27, 0x16}, // 331.000000MHz
        {0x4c, 0xbe, 0xe3, 0x5a, 0x4c, 0x9d, 0xd8, 0x19}, // 332.000000MHz
        {0x4c, 0xf9, 0x94, 0x5e, 0x4c, 0xd8, 0x89, 0x1d}, // 333.000000MHz
        {0x4d, 0x34, 0x46, 0x52, 0x4d, 0x13, 0x3b, 0x11}, // 334.000000MHz
        {0x4d, 0x6f, 0xf7, 0x55, 0x4d, 0x4e, 0xec, 0x14}, // 335.000000MHz
        {0x4d, 0xab, 0xa8, 0x59, 0x4d, 0x89, 0x9d, 0x18}, // 336.000000MHz
        {0x4d, 0xe6, 0x59, 0x5d, 0x4d, 0xc4, 0x4e, 0x1c}, // 337.000000MHz
        {0x4e, 0x21, 0x0b, 0x51, 0x4e, 0x00, 0x00, 0x10}, // 338.000000MHz
        {0x4e, 0x5c, 0xbc, 0x54, 0x4e, 0x3b, 0xb1, 0x13}, // 339.000000MHz

        {0x42, 0x3d, 0x28, 0x13, 0x42, 0x27, 0x76, 0x12}, // 430.000000MHz, [35], total 10
        {0x42, 0x64, 0x9e, 0x15, 0x42, 0x4e, 0xec, 0x14}, // 431.000000MHz
        {0x42, 0x8c, 0x14, 0x18, 0x42, 0x76, 0x62, 0x17}, // 432.000000MHz
        {0x42, 0xb3, 0x8a, 0x1a, 0x42, 0x9d, 0xd8, 0x19}, // 433.000000MHz
        {0x42, 0xdb, 0x00, 0x1d, 0x42, 0xc4, 0x4e, 0x1c}, // 434.000000MHz
        {0x42, 0x02, 0x77, 0x1f, 0x42, 0xec, 0xc4, 0x1e}, // 435.000000MHz
        {0x43, 0x29, 0xed, 0x11, 0x43, 0x13, 0x3b, 0x11}, // 436.000000MHz
        {0x43, 0x51, 0x63, 0x14, 0x43, 0x3b, 0xb1, 0x13}, // 437.000000MHz
        {0x43, 0x78, 0xd9, 0x16, 0x43, 0x62, 0x27, 0x16}, // 438.000000MHz
        {0x43, 0x9f, 0x4f, 0x19, 0x43, 0x89, 0x9d, 0x18}, // 439.000000MHz

        {0x48, 0x58, 0x7d, 0x5c, 0x48, 0x37, 0x72, 0x1b}, // 315.100006MHz, [45], total 9
        {0x48, 0xde, 0xdb, 0x5c, 0x48, 0xbd, 0xd0, 0x1b}, // 315.200006MHz
        {0x48, 0x64, 0x3a, 0x5d, 0x48, 0x42, 0x2f, 0x1c}, // 315.300006MHz
        {0x48, 0xea, 0x98, 0x5d, 0x48, 0xc8, 0x8d, 0x1c}, // 315.400006MHz
        {0x48, 0x6f, 0xf7, 0x5d, 0x48, 0x4e, 0xec, 0x1c}, // 315.500006MHz
        {0x48, 0xf5, 0x55, 0x5e, 0x48, 0xd4, 0x4a, 0x1d}, // 315.600006MHz
        {0x48, 0x7b, 0xb4, 0x5e, 0x48, 0x5a, 0xa9, 0x1d}, // 315.700006MHz
        {0x48, 0x01, 0x13, 0x5f, 0x48, 0xe0, 0x07, 0x1e}, // 315.800006MHz
        {0x48, 0x87, 0x71, 0x5f, 0x48, 0x66, 0x66, 0x1e}, // 315.900006MHz

    	{0x42, 0xb7, 0xc9, 0x1a, 0x42, 0xa1, 0x17, 0x1a}, // 433.100006MHz, [54], total 9
        {0x42, 0xbb, 0x08, 0x1b, 0x42, 0xa5, 0x56, 0x1a}, // 433.200006MHz
        {0x42, 0xbf, 0x47, 0x1b, 0x42, 0xa9, 0x95, 0x1a}, // 433.300006MHz
        {0x42, 0xc3, 0x86, 0x1b, 0x42, 0xad, 0xd4, 0x1a}, // 433.400006MHz
        {0x42, 0xc7, 0xc5, 0x1b, 0x42, 0xb1, 0x13, 0x1b}, // 433.500006MHz
        {0x42, 0xcb, 0x04, 0x1c, 0x42, 0xb5, 0x52, 0x1b}, // 433.600006MHz
        {0x42, 0xcf, 0x43, 0x1c, 0x42, 0xb9, 0x91, 0x1b}, // 433.700006MHz
        {0x42, 0xd3, 0x82, 0x1c, 0x42, 0xbd, 0xd0, 0x1b}, // 433.800006MHz
        {0x42, 0xd7, 0xc1, 0x1c, 0x42, 0xc0, 0x0f, 0x1c}, // 433.900006MHz
    };
    struct {
        unsigned int base_freq;
        unsigned int step;
        int offset;
        int total;
    } fb_cfg[4] = {
        {
            .base_freq = 305000U,
            .step = 1000U,
            .offset = 0,
            .total = 35,
        },
        {
            .base_freq = 430000U,
            .step = 1000U,
            .offset = 35,
            .total = 10,
        },
        {
            .base_freq = 315100U,
            .step = 100U,
            .offset = 45,
            .total = 9,
        },
        {
            .base_freq = 433100U,
            .step = 100U,
            .offset = 54,
            .total = 9,
        }
    };
    int i, j;

    if (maxsize != 8) {
    	tc_log_err("output size %d err", maxsize);
    	return -1;
    }

    for (i = 0; i < DeepOfArray(fb_cfg); ++i) {
        for (j = 0; j < fb_cfg[i].total; ++j) {
            // tc_log("total %d base_freq %d step %d freq_rf %d map_freq %d", fb_cfg[i].total, fb_cfg[i].base_freq, fb_cfg[i].step, \
            //         freq_rf, fb_cfg[i].base_freq + j * fb_cfg[i].step);
            if ((fb_cfg[i].base_freq + j * fb_cfg[i].step) == freq_rf) {
                // tc_log("return index %d + %d = %d", j, fb_cfg[i].offset, j + fb_cfg[i].offset);
                // tc_log("j %d", j);
                // hexdump(fb_map[j + fb_cfg[i].offset], 8, "fb_map");
                // return &fb_map[j + fb_cfg[i].offset][0];
                memcpy(output, fb_map[j + fb_cfg[i].offset], 8);
                return 8;
            }
        }
    }
    return -2;
}

static int bl3361_fill_map_freq_reg(unsigned int freq_rf, unsigned char *output, int maxsize)
{
    unsigned char reg_data[] = {
        // [CMT Bank] 
        0x00, 0x66, 0xEC, 0x1D, 0x70, 0x80, 0x14, 0x08, 0x91, 0x02, 0x02, 0xD0,
        // [System Bank] 
        0xAE, 0xE0, 0x35, 0x00, 0x00, 0xF4, 0x10, 0xE2, 0x42, 0x20, 0x21, 0x81,
        // [Frequency Bank] 
        0x46, 0x83, 0x32, 0x57, 0x46, 0x62, 0x27, 0x16,
        // [Data Rate Bank] 
        0xD3, 0x64, 0xA0, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xC0, 0x28, 0x0A, 0x53, 0x17, 0x07, 0x3C, 0x2F, 0x00, 0x26, 0x05, 0x05,
        // [Baseband Bank] 
        0x10, 0x08, 0x00, 0xAA, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0x2D, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xFF, 0x00, 0x00, 0x1F, 0x10,
        // [TX Bank] 
        0x55, 0xB9, 0x04, 0x00, 0x03, 0x70, 0x00, 0xB4, 0x1E, 0x3F, 0x7F,   // default 315MHz +-
    };

    const unsigned char tx_bank_433[11] = {
        0x55, 0x26, 0x03, 0x00, 0x03, 0x70, 0x00, 0x8A, 0x18, 0x3F, 0x7F  // 433MHz +-
    };

    if (maxsize < (sizeof(reg_data) * 2)) {
        tc_log_err("maxsize %d sizeof(reg_data) %d", maxsize, sizeof(reg_data));
        return -1;
    }

    int ret = bl3361_frequency_bank_mapping(freq_rf, &reg_data[BL3361_FREQ_BANK_REG], 8);
    if (ret < 0) {
        tc_log_err("bl3361_frequency_bank_mapping empty");
        return -2;
    }

    // hexdump(&reg_data[BL3361_FREQ_BANK_REG], 8, "Frequency Bank %d.%dMHz", freq_rf / 10, freq_rf % 10);

    // update TX BANK
    if (freq_rf > 340000U) 
        memcpy(&reg_data[BL3361_TX_BANK_REG], tx_bank_433, sizeof(tx_bank_433));

    int reg, offset;

    for (reg = 0x00, offset = 0; reg < 0x60; ++reg) {
        // tc_log("[0x%02x] 0x%02x offset %d output %p", reg, reg_data[reg], offset, output);
        output[offset++] = reg;
        output[offset++] = reg_data[reg];
    }
    return offset;
}

static unsigned char bl3361_afc_ovf_th(unsigned int freq_rf, int step, int channel)
{
    if (freq_rf == 305000U && step == 500 && channel <= 70)
        return 12;
    else if (freq_rf >= 305000U && freq_rf < 316000U && step == 100 && channel <= 20)
        return 11;
    else if (freq_rf >= 316000U && freq_rf <= 340000U && step == 100 && channel <= 20)
        return 12;
    else if (freq_rf >= 315000U && freq_rf <= 315900U && step == 10 && channel <= 20) {
        if (freq_rf == 315900U) {
            if (channel == 14)
                return 9;
            else if (channel == 15)
                return 0;
            else if (channel == 16)
                return 8;
            else 
                return 11;
        }
        else
            return 11;
    }
    else if (freq_rf == 430000U && step == 500 && channel <= 20)
        return 11;
    else if (freq_rf >= 430000U && freq_rf < 435000U && step == 100 && channel <= 20)
        return 10;
    else if (freq_rf >= 435000U && freq_rf <= 439000U && step == 100 && channel <= 20) {
        if (freq_rf == 435000U && channel == 2)
            return 10;
        else
            return 11;
    }
    else if (freq_rf >= 433000U && freq_rf <= 433900U && step == 10 && channel <= 20) {
        return 10;
    }
    else {
        tc_log_err("freq_rf %d.%03d MHz err", freq_rf / 1000, freq_rf % 1000);
        return 0xff;
    }
}

/*  create bl3361 rf setupo message
 *  input parameter
 *  @freq_rf: base frequency, unit 1KHz, range should be 305MHz - 340MHz or 430MHz - 440MHz
 *          eg: 305000(305MHz), 315200(315.2MHz), 433900(433.9MHz)
 *  @step: frequency increase step, eg: 1000KHz, 100KHz, 10Khz
 *  @channel: the channel range on base freqency, generally should be 10 or 20
 */
int bl3361_setup_rfcfg(unsigned int freq_rf, int step, int channel, unsigned char *output, int maxsize)
{
    int offset = 0, ret;
    struct tlv *tlv;

    tc_log("enter freq_rf %d.%03d MHz", freq_rf / 1000, freq_rf % 1000);

    // fill tag 01
    tlv = (struct tlv *)(output + offset);
#ifdef USING_CALCULATION
    if ((ret = bl3361_fill_calc_freq_reg(freq_rf / 1000.0F, tlv->value, maxsize - offset - offsetof(struct tlv, value))) < 0) {	
#else
    if ((ret = bl3361_fill_map_freq_reg(freq_rf, tlv->value, maxsize - offset - offsetof(struct tlv, value))) < 0) {
#endif
        tc_log_err("bl3361_fill_freq_reg ret %d", ret);
        return ret;
    }
    
    tlv->tag = 0x01;
    tlv->len = ret;
    // hexdump(tlv->value, tlv->len, "reg size %d", tlv->len);
    offset += offsetof(struct tlv, value) + tlv->len;

    // fill tag 06
    tlv = (struct tlv *)(output + offset);
    if ((ret = bl3361_afc_ovf_th(freq_rf, step, channel)) < 0) {
        tc_log_err("bl3361_afc_ovf_th ret %d", ret);
        return ret;
    }

    tlv->tag = 0x06;
    tlv->len = 2;
    tlv->value[0] = ret;
    tlv->value[1] = (unsigned char)(step * 10 / 25); // "step / 2.5"
    offset += offsetof(struct tlv, value) + tlv->len;

    // fill tag 07
    tlv = (struct tlv *)(output + offset);
    tlv->tag = 0x07;
    tlv->len = 1;
    tlv->value[0] = 0x10;
    offset += offsetof(struct tlv, value) + tlv->len;

    // fill tag 08
    tlv = (struct tlv *)(output + offset);
    tlv->tag = 0x08;
    tlv->len = 1;
    tlv->value[0] = 0x00;
    offset += offsetof(struct tlv, value) + tlv->len;

    return offset;
}

/* End of file */



